
Norm.Field = {};

Norm.Field.Primitive = function(sqlType) {
	
	var field = function(options) {
		this.options = options || {};
	};
	
	field.prototype =  {
		
		toSql : function() {
			
			var opts = this.options;
			return  sqlType +
				(opts.maxLength ? "(" + opts.maxLength + ") " : "") + 
				(opts.notNull ? " NOT NULL " : "") +
				(opts.defaultValue ? " DEFAULT " + opts.defaultValue + " ": "") +
				(opts.unique ? " UNIQUE " : "");
		},
		
		isBackwardRelation : false,
		isRelation : false
	};
	
	field.constructor = Norm.Field.Primitive;
	return field;
};

Norm.Fields = {
	
	String : new Norm.Field.Primitive("VARCHAR"),
	Integer : new Norm.Field.Primitive("INTEGER"),
	Float : new Norm.Field.Primitive("REAL"),
	TimeStamp : new Norm.Field.Primitive("TIMESTAMP")
};

Norm.Fields.ManyToOne = function(options) {
	this.options = options;	
};

Norm.Fields.ManyToOne.prototype = {
	
	copy : function() {
		return new Norm.Fields.ManyToOne(this.options); 
	},
	
	toSql : function() {
		return "";
	},
	
	select : function(whereExpression,params) {
		return this._selectRemove(whereExpression, params, "select");
	},
	
	remove : function(whereExpression,params) {
		return this._selectRemove(whereExpression, params, "remove");
	},
	
	getRelatedClass : function() {
		return Norm._models[this.options.related];
	},
	
	_selectRemove : function(whereExpression, params, method) {
		var fieldName = this.getRelatedClass() == this.modelClass ? this.options.related + "_id" : "rowid";
		params = params || [];
		params.unshift(this.instance.rowid);
		return this.getRelatedClass()[method](this.options.related + "." + fieldName + " = ? " + (whereExpression ? " AND " + whereExpression : "") , params);
	},
	
	isBackwardRelation : true,
	isRelation : true
};

Norm.Fields.OneToMany = function(options) {
	this.options = options;
};

Norm.Fields.OneToMany.prototype = {

	copy : function() {
		return new Norm.Fields.OneToMany(this.options); 
	},
	
	toSql : function() {
		return Norm.Sql.oneToManySql(this.options.related, this.options.allowNull);
	},
	
	getRelatedClass : function() {
		return Norm._models[this.options.related];
	},
	
	isBackwardRelation : false,
	isRelation : true
};

Norm.Fields.ManyToMany = function(options) {
	this.options = options;
};

Norm.Fields.ManyToMany.prototype = {
	
	_m2mModel : null,
	
	copy : function() {
		return new Norm.Fields.ManyToMany(this.options); 
	},
	
	getRelationModel : function() {
	
		if (! this._m2mModel) {
			var names = [this.modelClass.options.name, this.options.related].sort();
			var thisModelName = names[0];
			var relatedModelName = names[1];
			
			var fields = {};
			fields[relatedModelName] = new Norm.Fields.OneToMany({related : relatedModelName, onDeleteCascade : true});
			fields[thisModelName] = new Norm.Fields.OneToMany({related : thisModelName, onDeleteCascade : true});
			
			this._m2mModel = new Norm.Model({
				name : "m2m_" + thisModelName + "_" + relatedModelName,
				fields : fields
			});
		}
		return this._m2mModel;
	},
	
	add : function(relatedObject) {
	
		var m2mTable = this.getRelationModel();
		var relation = new m2mTable();
		relation[this.modelClass.options.name] = this.instance;
		relation[this.getRelatedClass().options.name] = relatedObject;
		relation.save();
		return relation;
	},
	
	toSql : function() {
		return "";
	},
	
	createTable : function() {
		
		if (Norm._models[this.options.related]) {
			this.getRelationModel().createTable();
		}
	},
	
	select : function(whereExpression, params) {
		
		var m2mTableName = this.getRelationModel().options.name;
		var relatedClass = this.getRelatedClass();
		return new Norm.ResultIterator(Norm.Database.execute(Norm.Sql.selectWithManyToMany(this.modelClass, relatedClass, m2mTableName, whereExpression, this.instance.rowid), params), relatedClass);
	},
	
	remove : function(related) {
		
		var relatedName = this.getRelatedClass().options.name; 
		return this.getRelationModel().remove(relatedName + "_id = ?", [typeof(related) == "number" ? related : related.rowid]);
	},
	
	dropTable : function() {
		this.getRelationModel().dropTable();
	},
	
	getRelatedClass : function() {
		return Norm._models[this.options.related];
	},
	
	isBackwardRelation : true,
	isRelation : true
};
